/*
 * This file is part of Ember.
 *
 * Ember is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Ember is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Ember.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Copyright (C) Grant Tyra 2010
 * granttyra@gmail.com
*/

#include "ember_string.h"
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <math.h>
#include <stdio.h>

char *EmberStringCreate(int size) {
    int i = 0;
    char *string = calloc(size, sizeof(char));
    
    for (i = 0; i < size - 1; i++) {
        string[i] = ' ';
    }
    
    string[size - 1] = '\0';
	return string;
}

char *EmberStringFromInt(int value) {
    char *buffer = EmberStringCreate(10);
    sprintf(buffer, "%i", value);
	return buffer;
}

void EmberStringExpand(char **cstr, int size) {
    cstr = realloc(cstr, sizeof(char) * size);
}

void EmberStringDestroy(char *cstr) {
    free(cstr);
}

char *EmberStringCopy(const char *cstr) {
	int size = strlen(cstr) + 1;
	char *copy = calloc(size, sizeof(char));
	strcpy(copy, cstr);
	return copy;
}

char *EmberStringConcat(const char *cstr1, const char *cstr2) {
	char *string = calloc(strlen(cstr1) + strlen(cstr2) + 1, sizeof(char));
	strcpy(string, cstr1);
	strcat(string, cstr2);
	return string;
}

void EmberStringAppend(char **cstr1, const char *cstr2) {
	int size = strlen(*cstr1) + strlen(cstr2) + 1;
	*cstr1 = realloc(*cstr1, size);
	strcat(*cstr1, cstr2);
}

void EmberStringSplit(const char *cstr, char c, EmberList *parts) {
	int i = 0;
	int begin = 0;
	int length = 0;
	int ignore = 0;
    char *substr = NULL;

	length = strlen(cstr);

	for (i = 0; i < length; i++) {
		if (cstr[i] == '"') {
			ignore = !ignore;
		}

		if (cstr[i] == '[') {
			ignore = 1;
		} else if (cstr[i] == ']') {
			ignore = 0;
		}

		if (cstr[i] == '{') {
			ignore = 1;
		} else if (cstr[i] == '}') {
			ignore = 0;
		}

		if (i - begin > 0 && cstr[i] == c && !ignore) {
			substr = EmberStringSubstring(cstr, begin, i);
            EmberListAdd(parts, substr);
			begin = i + 1;
		}
	}

	if (begin < length) {
		substr = EmberStringSubstring(cstr, begin, length);
        EmberListAdd(parts, substr);
	}
}

char *EmberStringSubstring(const char *cstr, int begin, int end) {
	int length = end - begin;
	char *string = malloc(sizeof(char) * (length + 1));
	memcpy(string, cstr + begin, length);
	string[length] = '\0';
	return string;
}

char *EmberStringReverseBetween(const char *cstr, char first, char last) {
	int begin = -1;
	int end = -1;
	int i = 0;
	int depth = 1;
	int length = strlen(cstr);

	for (i = length - 1; i >= 0; i--) {
		if (cstr[i] == last) {
			end = i;
			break;
		}
	}

	if (end < 0) {
		return NULL;
	}

	for (i = end - 1; i >= 0; i--) {
		if (cstr[i] == first) {
			depth--;

			if (depth <= 0) {
				begin = i;
				break;
			}
		} else if (cstr[i] == last) {
			depth++;
		}
	}

	if (begin < 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, begin + 1, end);
}

char *EmberStringBetween(const char *cstr, char first, char last) {
	int begin = -1;
	int end = -1;
	int i = 0;
	int depth = 1;
	int length = strlen(cstr);

	for (i = 0; i < length; i++) {
		if (cstr[i] == first) {
			begin = i;
			break;
		}
	}

	if (begin < 0) {
		return NULL;
	}

	for (i = begin + 1; i < length; i++) {
		if (cstr[i] == last) {
			depth--;

			if (depth <= 0) {
				end = i;
				break;
			}
		} else if (cstr[i] == first) {
			depth++;
		}
	}

	if (end < 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, begin + 1, end);
}

void EmberStringTrim(char **cstr) {
	int begin = 0;
	int end = 0;
	int length = 0;
	char *string = *cstr;
	char *temp = NULL;

	if (string == NULL) {
		return;
	}

	length = strlen(string);

	for (begin = 0; begin < length; begin++) {
		if (!isspace(string[begin])) {
			break;
		}
	}

	for (end = length - 1; end >= 0; end--) {
		if (!isspace(string[end])) {
			break;
		}
	}

	if (begin == 0 && end == length - 1) {
		return;
	}
	
	if (end - begin + 1 <= 0) {
		return;
	}

	temp = EmberStringSubstring(string, begin, end + 1);
	free(string);
	*cstr = temp;
}

int EmberStringStartsWithString(const char *cstr1, const char *cstr2) {
	return EmberStringFindString(cstr1, cstr2) == 0;
}

int EmberStringEndsWithString(const char *cstr1, const char *cstr2) {
	int len1 = strlen(cstr1);
	int len2 = strlen(cstr2);

	if (len2 > len1) {
		return 0;
	}

	return EmberStringFindString(cstr1, cstr2) == len1 - len2;
}

int EmberStringFind(const char *cstr, char c) {
    int i = 0;
	int length = strlen(cstr);

    for (i = 0; i < length; i++) {
        if (cstr[i] == c) {
            return i;
        }
    }

    return -1;
}

int EmberStringReverseFind(const char *cstr, char c) {
    int i = 0;
	int length = strlen(cstr);

    for (i = length - 1; i >= 0; i--) {
        if (cstr[i] == c) {
            return i;
        }
    }

    return -1;
}

char *EmberStringBefore(const char *cstr, char c) {
	int index = EmberStringFind(cstr, c);

	if (index <= 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, 0, index);
}

char *EmberStringReverseBefore(const char *cstr, char c) {
	int index = EmberStringReverseFind(cstr, c);

	if (index <= 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, 0, index);
}

char *EmberStringAfter(const char *cstr, char c) {
	int index = EmberStringFind(cstr, c);

	if (index <= 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, index + 1, strlen(cstr));
}

char *EmberStringReverseAfter(const char *cstr, char c) {
	int index = EmberStringReverseFind(cstr, c);

	if (index <= 0) {
		return NULL;
	}

	return EmberStringSubstring(cstr, index + 1, strlen(cstr));
}

char *EmberStringAfterString(const char *cstr1, const char *cstr2) {
	int index = EmberStringFindString(cstr1, cstr2);
	return EmberStringSubstring(cstr1, index + strlen(cstr2), strlen(cstr1));
}

char *EmberStringBeforeString(const char *cstr1, const char *cstr2) {
	int index = EmberStringFindString(cstr1, cstr2);
	return EmberStringSubstring(cstr1, 0, index);
}

int EmberStringIsEmpty(const char *cstr) {
	int i = 0;
	int length = strlen(cstr);

	for (i = 0; i < length; i++) {
		if (!isspace(cstr[i])) {
			return 0;
		}
	}

	return 1;
}

int EmberStringReverseFindString(const char *cstr1, const char *cstr2) {
	int i = 0;
	int j = 0;
	int index = -1;
	int len1 = strlen(cstr1);
	int len2 = strlen(cstr2);

	if (len1 < len2) {
		return -1;
	}

	for (i = len1 - 1; i >= 0; i--) {
		if (cstr1[i] != cstr2[0] || len1 - i < len2) {
			continue;
		}

		index = i;
		for (j = 0; j < len2; j++) {
			if (cstr1[i + j] != cstr2[j]) {
				index = -1;
				break;
			}
		}

		if (index >= 0) {
			return index;
		}
	}

	return -1;
}

int EmberStringFindString(const char *cstr1, const char *cstr2) {
	int i = 0;
	int j = 0;
	int index = -1;
	int len1 = strlen(cstr1);
	int len2 = strlen(cstr2);

	if (len1 < len2) {
		return -1;
	}

	for (i = 0; i < len1; i++) {
		if (cstr1[i] != cstr2[0] || len1 - i < len2) {
			continue;
		}

		index = i;
		for (j = 0; j < len2; j++) {
			if (cstr1[i + j] != cstr2[j]) {
				index = -1;
				break;
			}
		}

		if (index >= 0) {
			return index;
		}
	}

	return -1;
}

int EmberStringCount(const char *cstr, char c) {
    int i = 0;
	int count = 0;
	int length = strlen(cstr);

    for (i = 0; i < length; i++) {
        if (cstr[i] == c) {
            count++;
        }
    }

    return count;
}

int EmberStringIsValidDecimal(const char *cstr) {
    int i = 0;
	int length = strlen(cstr);

    for (i = 0; i < length; i++) {
        if (!isdigit(cstr[i]) && cstr[i] != '.') {
            return 0;
        }
    }

    return 1;
}

int EmberStringIsValidInteger(const char *cstr) {
    int i = 0;
	int length = strlen(cstr);

    for (i = 0; i < length; i++) {
        if (!isdigit(cstr[i])) {
            return 0;
        }
    }

    return 1;
}

int EmberStringHash(const char *cstr) {
	unsigned long hash = 5381;
	int c;
	while ((c = *cstr++)) hash = ((hash << 5) + hash) + c;
	return abs(hash);
}

int EmberStringStartsWith(const char *cstr, char c) {
	int i = 0;
	int length = strlen(cstr);

    for (i = 0; i < length; i++) {
		if (!isspace(cstr[i])) {
			if (cstr[i] == c) {
				return 1;
			}

			return 0;
		}
    }

	return 0;
}

int EmberStringContains(const char *cstr, char c) {
	return EmberStringFind(cstr, c) >= 0;
}

int EmberStringReverseContains(const char *cstr, char c) {
	return EmberStringReverseFind(cstr, c) >= 0;
}

int EmberStringContainsString(const char *cstr1, const char *cstr2) {
	return EmberStringFindString(cstr1, cstr2) >= 0;
}

int EmberStringReverseContainsString(const char *cstr1, const char *cstr2) {
	return EmberStringReverseFindString(cstr1, cstr2) >= 0;
}
